home *** CD-ROM | disk | FTP | other *** search
/ PC go! 2018 January / PCgo 01-2018 CD-ROM Germany.iso / nw.pak / Unnamed File 000130.txt < prev    next >
Encoding:
Text File  |  2015-07-29  |  6.0 KB  |  206 lines

  1. // Copyright 2014 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4.  
  5. cr.define('cr.ui', function() {
  6.   /**
  7.    * A class to manage focus between given horizontally arranged elements.
  8.    * For example, given the page:
  9.    *
  10.    *   <input type="checkbox"> <label>Check me!</label> <button>X</button>
  11.    *
  12.    * One could create a FocusRow by doing:
  13.    *
  14.    *   new cr.ui.FocusRow([checkboxEl, labelEl, buttonEl])
  15.    *
  16.    * if there are references to each node or querying them from the DOM like so:
  17.    *
  18.    *   new cr.ui.FocusRow(dialog.querySelectorAll('list input[type=checkbox]'))
  19.    *
  20.    * Pressing left cycles backward and pressing right cycles forward in item
  21.    * order. Pressing Home goes to the beginning of the list and End goes to the
  22.    * end of the list.
  23.    *
  24.    * If an item in this row is focused, it'll stay active (accessible via tab).
  25.    * If no items in this row are focused, the row can stay active until focus
  26.    * changes to a node inside |this.boundary_|. If opt_boundary isn't
  27.    * specified, any focus change deactivates the row.
  28.    *
  29.    * @param {!Array.<!Element>|!NodeList} items Elements to track focus of.
  30.    * @param {Node=} opt_boundary Focus events are ignored outside of this node.
  31.    * @param {FocusRow.Delegate=} opt_delegate A delegate to handle key events.
  32.    * @param {FocusRow.Observer=} opt_observer An observer that's notified if
  33.    *     this focus row is added to or removed from the focus order.
  34.    * @constructor
  35.    */
  36.   function FocusRow(items, opt_boundary, opt_delegate, opt_observer) {
  37.     /** @type {!Array.<!Element>} */
  38.     this.items = Array.prototype.slice.call(items);
  39.     assert(this.items.length > 0);
  40.  
  41.     /** @type {!Node} */
  42.     this.boundary_ = opt_boundary || document;
  43.  
  44.     /** @private {cr.ui.FocusRow.Delegate|undefined} */
  45.     this.delegate_ = opt_delegate;
  46.  
  47.     /** @private {cr.ui.FocusRow.Observer|undefined} */
  48.     this.observer_ = opt_observer;
  49.  
  50.     /** @private {!EventTracker} */
  51.     this.eventTracker_ = new EventTracker;
  52.     this.eventTracker_.add(cr.doc, 'focusin', this.onFocusin_.bind(this));
  53.     this.eventTracker_.add(cr.doc, 'keydown', this.onKeydown_.bind(this));
  54.  
  55.     this.items.forEach(function(item) {
  56.       if (item != document.activeElement)
  57.         item.tabIndex = -1;
  58.  
  59.       this.eventTracker_.add(item, 'mousedown', this.onMousedown_.bind(this));
  60.     }, this);
  61.  
  62.     /**
  63.      * The index that should be actively participating in the page tab order.
  64.      * @type {number}
  65.      * @private
  66.      */
  67.     this.activeIndex_ = this.items.indexOf(document.activeElement);
  68.   }
  69.  
  70.   /** @interface */
  71.   FocusRow.Delegate = function() {};
  72.  
  73.   FocusRow.Delegate.prototype = {
  74.     /**
  75.      * Called when a key is pressed while an item in |this.items| is focused. If
  76.      * |e|'s default is prevented, further processing is skipped.
  77.      * @param {cr.ui.FocusRow} row The row that detected a keydown.
  78.      * @param {Event} e
  79.      * @return {boolean} Whether the event was handled.
  80.      */
  81.     onKeydown: assertNotReached,
  82.  
  83.     /**
  84.      * @param {cr.ui.FocusRow} row The row that detected the mouse going down.
  85.      * @param {Event} e
  86.      * @return {boolean} Whether the event was handled.
  87.      */
  88.     onMousedown: assertNotReached,
  89.   };
  90.  
  91.   /** @interface */
  92.   FocusRow.Observer = function() {};
  93.  
  94.   FocusRow.Observer.prototype = {
  95.     /**
  96.      * Called when the row is activated (added to the focus order).
  97.      * @param {cr.ui.FocusRow} row The row added to the focus order.
  98.      */
  99.     onActivate: assertNotReached,
  100.  
  101.     /**
  102.      * Called when the row is deactivated (removed from the focus order).
  103.      * @param {cr.ui.FocusRow} row The row removed from the focus order.
  104.      */
  105.     onDeactivate: assertNotReached,
  106.   };
  107.  
  108.   FocusRow.prototype = {
  109.     get activeIndex() {
  110.       return this.activeIndex_;
  111.     },
  112.     set activeIndex(index) {
  113.       var wasActive = this.items[this.activeIndex_];
  114.       if (wasActive)
  115.         wasActive.tabIndex = -1;
  116.  
  117.       this.items.forEach(function(item) { assert(item.tabIndex == -1); });
  118.       this.activeIndex_ = index;
  119.  
  120.       if (this.items[index])
  121.         this.items[index].tabIndex = 0;
  122.  
  123.       if (!this.observer_)
  124.         return;
  125.  
  126.       var isActive = index >= 0 && index < this.items.length;
  127.       if (isActive == !!wasActive)
  128.         return;
  129.  
  130.       if (isActive)
  131.         this.observer_.onActivate(this);
  132.       else
  133.         this.observer_.onDeactivate(this);
  134.     },
  135.  
  136.     /**
  137.      * Focuses the item at |index|.
  138.      * @param {number} index An index to focus. Must be between 0 and
  139.      *     this.items.length - 1.
  140.      */
  141.     focusIndex: function(index) {
  142.       this.items[index].focus();
  143.     },
  144.  
  145.     /** Call this to clean up event handling before dereferencing. */
  146.     destroy: function() {
  147.       this.eventTracker_.removeAll();
  148.     },
  149.  
  150.     /**
  151.      * @param {Event} e The focusin event.
  152.      * @private
  153.      */
  154.     onFocusin_: function(e) {
  155.       if (this.boundary_.contains(assertInstanceof(e.target, Node)))
  156.         this.activeIndex = this.items.indexOf(e.target);
  157.     },
  158.  
  159.     /**
  160.      * @param {Event} e A focus event.
  161.      * @private
  162.      */
  163.     onKeydown_: function(e) {
  164.       var item = this.items.indexOf(e.target);
  165.       if (item < 0)
  166.         return;
  167.  
  168.       if (this.delegate_ && this.delegate_.onKeydown(this, e))
  169.         return;
  170.  
  171.       var index = -1;
  172.  
  173.       if (e.keyIdentifier == 'Left')
  174.         index = item + (isRTL() ? 1 : -1);
  175.       else if (e.keyIdentifier == 'Right')
  176.         index = item + (isRTL() ? -1 : 1);
  177.       else if (e.keyIdentifier == 'Home')
  178.         index = 0;
  179.       else if (e.keyIdentifier == 'End')
  180.         index = this.items.length - 1;
  181.  
  182.       if (!this.items[index])
  183.         return;
  184.  
  185.       this.focusIndex(index);
  186.       e.preventDefault();
  187.     },
  188.  
  189.     /**
  190.      * @param {Event} e A click event.
  191.      * @private
  192.      */
  193.     onMousedown_: function(e) {
  194.       if (this.delegate_ && this.delegate_.onMousedown(this, e))
  195.         return;
  196.  
  197.       if (!e.button)
  198.         this.activeIndex = this.items.indexOf(e.currentTarget);
  199.     },
  200.   };
  201.  
  202.   return {
  203.     FocusRow: FocusRow,
  204.   };
  205. });
  206.